import { h, VNode, render } from "vue";
import Loading from "./loading.vue";

export interface IVLoadingBinding {
  arg: HTMLElement | VNode | undefined;
  modifiers: {};
  value: boolean;
  oldValue: boolean;
}

type WeakMapKey = HTMLElement;

type WeakMapValue = {
  element: HTMLElement;
  visible: boolean;
  abort: AbortController;
};
//存储每个元素的loading状态
const loadingMap = new WeakMap<WeakMapKey, WeakMapValue>();

const createDOM = (el: HTMLElement, component?: any) => {
  let value = loadingMap.get(el);

  if (value) return;

  if (!value) {
    const container = document.createElement("div");
    container.className = "v-loading_container";
    document.body.appendChild(container);
    container.style.display = "none";

    const abort = new AbortController();
    loadingMap.set(el, {
      element: container,
      visible: false,
      abort,
    });
  }
  // 解决Non-function value encountered for default slot警告
  // 默认槽遇到非函数值。首选函数槽以获得更好的性能
  let node = component ? h(Loading, () => h(component)) : h(Loading);
  value = loadingMap.get(el);

  render(node, value?.element!);

  window.addEventListener(
    "resize",
    () => {
      const { visible } = loadingMap.get(el)!;
      if (!visible) return;

      const { top, left, width, height } = el.getBoundingClientRect();
      value!.element.style.left = left + "px";
      value!.element.style.top = top + "px";
      value!.element.style.width = width + "px";
      value!.element.style.height = height + "px";
    },
    {
      signal: value!.abort.signal,
    }
  );
};
const removeDOM = (el: HTMLElement) => {
  const value = loadingMap.get(el);
  value && document.body.removeChild(value.element);
  value?.abort.abort();
  loadingMap.delete(el);
};

export const vLoading = {
  mounted(el: HTMLElement, { value, arg }: IVLoadingBinding) {
    createDOM(el, arg);
    const { element, abort } = loadingMap.get(el)!;
    el.observable = new ResizeObserver((entires) => {
      for (const item of entires) {
        const current = loadingMap.get(el);

        if (!current?.visible) return;

        const { top, left, width, height } =
          item.target.getBoundingClientRect();

        element.style.position = "fixed";
        element.style.left = left + "px";
        element.style.top = top + "px";
        element.style.width = width + "px";
        element.style.height = height + "px";
        element.style.borderRadius = window.getComputedStyle(el).borderRadius;
      }
    });
    el.observable.observe(el);

    // 默认是false，如果初始化加载的时候是true的时候再更新
    value && loadingMap.set(el, { element, visible: value, abort });

    element.style.display = value ? "flex" : "node";
  },

  updated(el: HTMLElement, { oldValue, value }: IVLoadingBinding) {
    const { top, left, width, height } = el.getBoundingClientRect();

    const { element, abort } = loadingMap.get(el)!;
    element.style.position = "fixed";
    element.style.left = left + "px";
    element.style.top = top + "px";
    element.style.width = width + "px";
    element.style.height = height + "px";

    if (oldValue === value) {
      return;
    }
    loadingMap.set(el, {
      element,
      visible: value,
      abort,
    });
    element.style.display = value ? "flex" : "none";
  },

  unmounted(el: HTMLElement) {
    removeDOM(el);
  },
};
